package com.sheyu.mall.order.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sheyu.common.constant.CartConstant;
import com.sheyu.common.exception.NoStockException;
import com.sheyu.common.to.OrderTo;
import com.sheyu.common.to.SeckillOrderTo;
import com.sheyu.common.to.SkuHasStockVo;
import com.sheyu.common.utils.R;
import com.sheyu.common.vo.MemberResponseVo;
import com.sheyu.mall.order.constant.OrderConstant;
import com.sheyu.mall.order.entity.OmsOrderItemEntity;
import com.sheyu.mall.order.enume.OrderStatusEnum;
import com.sheyu.mall.order.feign.CartFeignService;
import com.sheyu.mall.order.feign.MemberfeignService;
import com.sheyu.mall.order.feign.ProductFeignService;
import com.sheyu.mall.order.feign.WareFeignService;
import com.sheyu.mall.order.interceptor.LoginUserInterceptor;
import com.sheyu.mall.order.service.OmsOrderItemService;
import com.sheyu.mall.order.to.OrderCreateTo;
import com.sheyu.mall.order.to.SpuInfoVo;
import com.sheyu.mall.order.vo.*;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sheyu.common.utils.PageUtils;
import com.sheyu.common.utils.Query;

import com.sheyu.mall.order.dao.OmsOrderDao;
import com.sheyu.mall.order.entity.OmsOrderEntity;
import com.sheyu.mall.order.service.OmsOrderService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;


@Service("omsOrderService")
public class OmsOrderServiceImpl extends ServiceImpl<OmsOrderDao, OmsOrderEntity> implements OmsOrderService {

    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal =  new ThreadLocal<>();
    @Autowired
    MemberfeignService memberfeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    WareFeignService wareFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OmsOrderItemService orderItemService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OmsOrderEntity> page = this.page(
                new Query<OmsOrderEntity>().getPage(params),
                new QueryWrapper<OmsOrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo comfirmOrder() throws ExecutionException, InterruptedException {

        /**
         * 构建orderConfirmVo
         * 1、获取收货地址
         * 2、获取所有选中的购物项
         * 3、获取优惠券（会员积分）
         */
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();

        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginuser.get();

        RequestAttributes  requestAttributes = RequestContextHolder.getRequestAttributes();

        //异步获取用户收货地址
        CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(()->{
            RequestContextHolder.setRequestAttributes(requestAttributes);
            //获取用户的所有收货地址
            List<MemberAddressVo> memberAddressVoList = memberfeignService.getAddress(memberResponseVo.getId());
            orderConfirmVo.setMemberAddressVos(memberAddressVoList);
        },executor);

        //异步获取购物车中的商品
        CompletableFuture<Void> orderItemFuture = CompletableFuture.runAsync(()->{
            RequestContextHolder.setRequestAttributes(requestAttributes);

            //获取当前购物车里面的商品
            List<OrderItemVo> orderItemVos = cartFeignService.getCurrentCartItems();
            orderConfirmVo.setItems(orderItemVos);
//            orderConfirmVo.setCount(orderConfirmVo.getCount());
//            orderConfirmVo.setTotal(orderConfirmVo.getTotal());
//            orderConfirmVo.setPayPrice(orderConfirmVo.getPayPrice());
        },executor).thenRunAsync(()->{
            List<OrderItemVo> orderItemVos = orderConfirmVo.getItems();
            //获取商品的skuId
            List<Long> skuIds = orderItemVos.stream().map(orderItemVo -> orderItemVo.getSkuId())
                    .collect(Collectors.toList());
            //获取商品的库存
            R skuHasStock = wareFeignService.getSkuHasStock(skuIds);

            //远程获取库存的结果
            List<SkuHasStockVo> skuHasStockVos = skuHasStock.getData("data",new TypeReference<List<SkuHasStockVo>>(){});
            if (skuHasStockVos!=null&&skuHasStockVos.size()>0) {
                Map<Long, Boolean> stocks = skuHasStockVos.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                orderConfirmVo.setStocks(stocks);
            }
        },executor);

        //获取积分（在memberResponseVo中携带）
        orderConfirmVo.setIntegration(memberResponseVo.getIntegration());

        //防止重复令牌（防止表单重复提交）
        String  token = UUID.randomUUID().toString().replace("-","");
        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberResponseVo.getId(),token,30, TimeUnit.MINUTES);
        orderConfirmVo.setOrderToken(token);
        CompletableFuture.allOf(addressFuture,orderItemFuture).get();


        return orderConfirmVo;
    }
    @Transactional(rollbackFor = Exception.class)
    @Override
    public SubmitOrderResponseVo submitOrder(OrderSubmitVo vo) {

        confirmVoThreadLocal.set(vo);

        SubmitOrderResponseVo responseVo = new SubmitOrderResponseVo();
        //去创建、下订单、验证令牌、验价格、锁定库存。。。

        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginuser.get();

        responseVo.setCode(0);

        //验证令牌是否合法【令牌的对比和删除必须保证原子性】
        String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

        String orderToken = vo.getOrderToken();

        Long result = redisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX+ memberResponseVo.getId()),
                orderToken);
        if (result == 0L){
            //令牌验证失败
            responseVo.setCode(1);
            return responseVo;
        }else{
            //令牌验证成功
            //1、创建订单、订单项等信息
            OrderCreateTo orderCreateTo = createOrder();

            BigDecimal payAmount = orderCreateTo.getOrder().getPayAmount();
            BigDecimal payPrice = vo.getPayPrice();
            if (Math.abs(payAmount.subtract(payPrice).doubleValue())<0.01){
                saveOrder(orderCreateTo);
                //4、库存锁定,只要有异常，回滚订单数据
                //订单号、所有订单项信息(skuId,skuNum,skuName)
                WareSkuLockVo lockVo = new WareSkuLockVo();
                lockVo.setOrderSn(orderCreateTo.getOrder().getOrderSn());

                List<OrderItemVo> orderItemVos = orderCreateTo.getOrderItems().stream().map(omsOrderItemEntity -> {
                    OrderItemVo orderItemVo = new OrderItemVo();
                    orderItemVo.setSkuId(omsOrderItemEntity.getSkuId());
                    orderItemVo.setCount(omsOrderItemEntity.getSkuQuantity());
                    orderItemVo.setTitle(omsOrderItemEntity.getSkuName());
                    return orderItemVo;
                }).collect(Collectors.toList());
                lockVo.setLocks(orderItemVos);
                R r = wareFeignService.orderLockStock(lockVo);
                if (r.getCode()==0){
                    responseVo.setOrder(orderCreateTo.getOrder());
                    //订单创建成功，发送消息给MQ
                    rabbitTemplate.convertAndSend("order-event-exchange",
                            "order.create.order",orderCreateTo.getOrder());
                    //删除购物车里面的数据
                    redisTemplate.delete(CartConstant.CART_PREFIX+memberResponseVo.getId());
                    return responseVo;
                }else{
                    String msg = (String) r.get("msg");
                    throw new NoStockException(msg);
                }
            }else{
                responseVo.setCode(2);
                return responseVo;
            }

        }
    }

    @Override
    public OmsOrderEntity getOrderStatus(String orderSn) {
        QueryWrapper<OmsOrderEntity> orderSnSql = new QueryWrapper<OmsOrderEntity>().eq("order_sn", orderSn);
        OmsOrderEntity orderEntity = this.baseMapper.selectOne(orderSnSql);
        return orderEntity;
    }

    /**
     * 关闭订单
     * @param orderEntity
     */
    @Override
    public void closeOrder(OmsOrderEntity orderEntity) {
        OmsOrderEntity orderInfo = this.getOne(new QueryWrapper<OmsOrderEntity>().eq("order_sn", orderEntity.getOrderSn()));

        if (orderInfo.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())){
            OmsOrderEntity orderUpdate = new OmsOrderEntity();
            orderUpdate.setId(orderInfo.getId());
            orderUpdate.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(orderUpdate);

            //发送消息给MQ

            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(orderInfo,orderTo);

            try{
                //TODO 确保每个消息发送成功，给每个消息做好日志记录，（给数据库保存每个详细信息）保存每个消息的详细信息
                rabbitTemplate.convertAndSend("order-event-exchange","order.release.other",orderTo);
            }catch (Exception e){
                //TODO 定期扫描数据库，重新发送失败的消息
            }
        }
    }

    @Override
    public void createSeckillOrder(SeckillOrderTo seckillOrderTo) {
        //1、保存订单信息
        OmsOrderEntity orderEntity = new OmsOrderEntity();
        orderEntity.setMemberId(seckillOrderTo.getMemberId());
        orderEntity.setCreateTime(new Date());
        orderEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderEntity.setPayAmount(seckillOrderTo.getSeckillPrice());
        orderEntity.setTotalAmount(BigDecimal.valueOf(seckillOrderTo.getNum()));
        BigDecimal totalPrice = seckillOrderTo.getSeckillPrice().multiply(BigDecimal.valueOf(seckillOrderTo.getNum()));
        orderEntity.setPayAmount(totalPrice);
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        this.save(orderEntity);

        //2、保存订单项信息
        OmsOrderItemEntity orderItemEntity = new OmsOrderItemEntity();
        orderItemEntity.setOrderSn(seckillOrderTo.getOrderSn());
        orderItemEntity.setSkuId(seckillOrderTo.getSkuId());
        orderItemEntity.setRealAmount(totalPrice);
        orderItemEntity.setSkuQuantity(seckillOrderTo.getNum());

        R spuInfo = productFeignService.getSpuInfoBySkuId(seckillOrderTo.getSkuId());

        SpuInfoVo spuInfoData = spuInfo.getData("data",new TypeReference<SpuInfoVo>(){});

        orderItemEntity.setSpuId(spuInfoData.getId());
        orderItemEntity.setSpuName(spuInfoData.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoData.getBrandName());
        orderItemEntity.setCategoryId(spuInfoData.getCatalogId());

        //保存订单项数据
        orderItemService.save(orderItemEntity);
    }

    private void saveOrder(OrderCreateTo orderCreateTo) {
        OmsOrderEntity orderEntity = orderCreateTo.getOrder();
        orderEntity.setModifyTime(new Date());
        orderEntity.setCreateTime(new Date());
        //保存订单
        this.baseMapper.insert(orderEntity);
        List<OmsOrderItemEntity> orderItems = orderCreateTo.getOrderItems();
        //批量保存订单单项数据
        orderItemService.saveBatch(orderItems);
    }

    private OrderCreateTo createOrder() {
        OrderCreateTo createTo = new OrderCreateTo();

        //1、生成订单号(雪花算法)
        String orderSn = IdWorker.getTimeId().substring(0,16);

        //根据ordersn生成订单信息
        OmsOrderEntity orderEntity = builderOrder(orderSn);

        //2、获取购物车所有的订单项组成orderSn
        List<OmsOrderItemEntity> orderItemEntities = builderOrderItems(orderSn);

        //重新更新用户的积分及成长值
        computePrice(orderEntity,orderItemEntities);

        createTo.setOrder(orderEntity);
        createTo.setOrderItems(orderItemEntities);
        return createTo;
    }

    private void computePrice(OmsOrderEntity orderEntity, List<OmsOrderItemEntity> orderItemEntities) {

        BigDecimal total = new BigDecimal("0.0");
        BigDecimal coupon = new BigDecimal("0.0");
        BigDecimal intergration = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");

        //积分、成长值
        Integer integrationTotal = 0;
        Integer growthTotal = 0;

        for(OmsOrderItemEntity orderItemEntity:orderItemEntities){
            coupon = coupon.add(orderItemEntity.getCouponAmount());
            promotion = promotion.add(orderItemEntity.getPromotionAmount());
            intergration = intergration.add(orderItemEntity.getIntegrationAmount());

            //总价
            total = total.add(orderItemEntity.getRealAmount());

            //积分、成长值
            integrationTotal += orderItemEntity.getGiftIntegration();
            growthTotal += orderItemEntity.getGiftGrowth();
        }

        //1、订单价格相关
        orderEntity.setTotalAmount(total);
        orderEntity.setPayAmount(total.add(orderEntity.getFreightAmount()));
        orderEntity.setCouponAmount(coupon);
        orderEntity.setPromotionAmount(promotion);
        orderEntity.setIntegrationAmount(intergration);

        //设置积分成长值信息
        orderEntity.setIntegration(integrationTotal);
        orderEntity.setGrowth(growthTotal);
        orderEntity.setDeleteStatus(0);
    }

    private List<OmsOrderItemEntity> builderOrderItems(String orderSn) {
        List<OmsOrderItemEntity> orderItemEntities = new ArrayList<>();

        List<OrderItemVo> currentCartItems = cartFeignService.getCurrentCartItems();

        if (currentCartItems!=null && currentCartItems.size()>0){
            orderItemEntities = currentCartItems.stream().map((item)->{
                OmsOrderItemEntity orderItemEntity = builderOrderItem(item);
                orderItemEntity.setOrderSn(orderSn);
                return orderItemEntity;
            }).collect(Collectors.toList());
        }
        return orderItemEntities;
    }

    private OmsOrderItemEntity builderOrderItem(OrderItemVo item) {
        OmsOrderItemEntity orderItemEntity = new OmsOrderItemEntity();

        Long skuId = item.getSkuId();

        R spuInfo = productFeignService.getSpuInfoBySkuId(skuId);

        SpuInfoVo spuInfoVo = spuInfo.getData("data",new TypeReference<SpuInfoVo>(){});

        orderItemEntity.setSpuId(spuInfoVo.getId());
        orderItemEntity.setSpuName(spuInfoVo.getSpuName());
        orderItemEntity.setSpuBrand(spuInfoVo.getBrandName());
        orderItemEntity.setCategoryId(spuInfoVo.getCatalogId());

        //2、商品的sku信息
        orderItemEntity.setSkuId(skuId);
        orderItemEntity.setSkuName(item.getTitle());
        orderItemEntity.setSkuPic(item.getImage());
        orderItemEntity.setSkuPrice(item.getPrice());
        orderItemEntity.setSkuQuantity(item.getCount());

        String skuAttrValues = StringUtils.collectionToDelimitedString(item.getSkuAttrValues(),";");
        orderItemEntity.setSkuAttrsVals(skuAttrValues);

        //4、商品的积分信息
        orderItemEntity.setGiftGrowth(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());
        orderItemEntity.setGiftIntegration(item.getPrice().multiply(new BigDecimal(item.getCount())).intValue());

        //5、订单项的价格信息
        orderItemEntity.setPromotionAmount(BigDecimal.ZERO);
        orderItemEntity.setCouponAmount(BigDecimal.ZERO);
        orderItemEntity.setIntegrationAmount(BigDecimal.ZERO);

        //当前订单项的实际金额
        BigDecimal origin = orderItemEntity.getSkuPrice().multiply(new BigDecimal(orderItemEntity.getSkuQuantity().toString()));
        //原价减去优惠得到最终的价格

        BigDecimal subtract = origin.subtract(orderItemEntity.getCouponAmount())
                .subtract(orderItemEntity.getIntegrationAmount())
                .subtract(orderItemEntity.getPromotionAmount());
        orderItemEntity.setRealAmount(subtract);
        return orderItemEntity;
    }

    /**
     * 构建订单数据
     * @param orderSn
     * @return
     */
    private OmsOrderEntity builderOrder(String orderSn) {
        //获取当前用户登陆信息
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginuser.get();

        OmsOrderEntity orderEntity = new OmsOrderEntity();

        orderEntity.setMemberId(memberResponseVo.getId());
        orderEntity.setOrderSn(orderSn);
        orderEntity.setMemberUsername(memberResponseVo.getUsername());

        OrderSubmitVo orderSubmitVo = confirmVoThreadLocal.get();

        R fareAddressVo = wareFeignService.getFare(orderSubmitVo.getAddrId());
        FareVo fareResp = fareAddressVo.getData("data",new TypeReference<FareVo>(){});

        //获取运费信息
        BigDecimal fare = fareResp.getFare();
        orderEntity.setFreightAmount(fare);

        //获取到收货地址和运费信息
        MemberAddressVo address  = fareResp.getAddress();

        //设置收货人信息
        orderEntity.setReceiverName(address.getName());
        orderEntity.setReceiverPhone(address.getPhone());
        orderEntity.setReceiverPostCode(address.getPostCode());
        orderEntity.setReceiverProvince(address.getProvince());
        orderEntity.setReceiverCity(address.getCity());
        orderEntity.setReceiverRegion(address.getRegion());
        orderEntity.setReceiverDetailAddress(address.getDetailAddress());

        //设置订单相关的状态信息
         orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
         orderEntity.setAutoConfirmDay(7);
         orderEntity.setConfirmStatus(0);

         return orderEntity;
    }

}
